using Unity.Physics;
using Unity.Transforms;
using UnityEngine;

namespace Gj
{
    public class MovePart : BasePart
    {
        private float _angle;
        private MoveStatus _lastMoveStatus = MoveStatus.None;
        private Vector3 _lookDirection = Vector3.zero;
        private Vector3 _lookPosition = Vector3.zero;
        private LookStatus _lookStatus = LookStatus.None;
        private UnityObject _lookTarget;
        private readonly float _maxFrameTime = 1f;
        private readonly float _minFrameTime = 0.2f;
        private Vector3 _moveDirection = Vector3.zero;
        private Vector3 _movePosition = Vector3.zero;
        private MoveStatus _moveStatus = MoveStatus.None;
        private UnityObject _moveTarget;
        private Notice _notice;
        private Vector3 _offsetPosition = Vector3.zero;
        private Move _self;
        private float _surplusFrameTime;
        private float _totalFrameTime;
        private bool autoLook;
        private readonly float findTime = 0.1f;
        private int lastIndex = -1;
        private Vector3 lastPosition;
        private float lastTime;
        private bool lockLook;
        private bool moving;
        private Vector3[] paths;

        private void OnDrawGizmos()
        {
            //           if (_movePosition == Vector3.zero && _self != null)
            //           {
            //               Gizmos.color = paths!=null && paths.Length > 0 ? Color.yellow : Color.cyan;
            //               Gizmos.DrawWireSphere (_self.GetPosition(), 1f);
            //           }
            //           if (paths!=null && paths.Length > 0)
            //           {
            //               Gizmos.color = Color.red;
            //               Gizmos.DrawWireSphere (paths[paths.Length-1], 0.5f);
            //                   Debug.DrawLine(_self.GetPosition(), paths[0], Color.green, 2f);
            //               for (var i=1;i< paths.Length;i++)
            //               {
            //                   Debug.DrawLine(paths[i-1], paths[i], Color.green, 2f);
            //               }
            //               Gizmos.color = Color.blue;
            //               Gizmos.DrawWireSphere (_movePosition, 0.8f);
            //           }
            //           if (_movePosition != Vector3.zero && !_self.IsAuto())
            //           {
            //               Gizmos.color = Color.yellow;
            //               Gizmos.DrawWireSphere (_movePosition, 2f);
            //           }
        }

        public void Set(Notice notice)
        {
            _notice = notice;
        }

        public void Init(Move target)
        {
            _self = target;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new MoveStatusData { status = MoveStatus.None });
            ObjectService.EntityManager.AddComponentData(entity, new MoveData());
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.None });
            ObjectService.EntityManager.AddComponentData(entity, new LookData());
        }

        public void Rotate(Vector3 direction, float speed)
        {
            if (autoLook) return;
            if (_lookStatus == LookStatus.Rotate && _lookDirection == direction) return;
            _lookDirection = direction;
            _lookStatus = LookStatus.Rotate;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Rotate });
            ObjectService.EntityManager.AddComponentData(entity, new LookData { direction = direction, speed = speed });
        }

        public void LookAngle(float angle)
        {
            if (autoLook) return;
            if (_lookStatus == LookStatus.Angle && _angle == angle) return;
            _angle = angle;
            _lookStatus = LookStatus.Angle;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Angle });
            ObjectService.EntityManager.AddComponentData(entity, new LookData { angle = angle });
        }

        public void LookLock()
        {
            lockLook = true;
        }

        public void LookUnLock()
        {
            lockLook = false;
            CancelLook();
        }

        public void LookDirection(Vector3 direction)
        {
            if (autoLook) return;
            if (_lookStatus == LookStatus.Direction && _lookDirection == direction) return;
            _lookDirection = direction;
            _lookStatus = LookStatus.Direction;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Direction });
            ObjectService.EntityManager.AddComponentData(entity, new LookData { direction = direction });
        }

        public void LookPosition(Vector3 position)
        {
            if (_lookStatus == LookStatus.Position && _lookPosition == position) return;
            _lookPosition = position;
            _lookStatus = LookStatus.Position;
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Position });
            ObjectService.EntityManager.AddComponentData(entity, new LookData { position = position });
            autoLook = true;
        }

        public void LookTarget(UnityObject target)
        {
            if (target == null) return;
            if (_lookStatus == LookStatus.Target && _lookTarget == target) return;
            _lookTarget = target;
            _lookStatus = LookStatus.Target;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.Target });
            ObjectService.EntityManager.AddComponentData(entity, new LookData { target = target.GetEntity() });
            autoLook = true;
        }

        public void CancelLook()
        {
            if (lockLook) return;
            _lookStatus = LookStatus.None;

            var entity = _self.GetEntity();
            if (ObjectService.EntityManager.Exists(entity))
                ObjectService.EntityManager.AddComponentData(entity, new LookStatusData { status = LookStatus.None });
            autoLook = false;
        }
        //       private void OnDrawGizmosSelected()
        //       {
        //           if (paths!=null && paths.Length > 0)
        //           {
        //               Gizmos.color = Color.red;
        //               Gizmos.DrawWireSphere (paths[paths.Length-1], 0.5f);
        //               Debug.DrawLine(_self.GetPosition(), paths[0], Color.magenta, 2f);
        //               for (var i=1;i< paths.Length;i++)
        //               {
        //                   Debug.DrawLine(paths[i-1], paths[i], Color.magenta, 2f);
        //               }
        //               Gizmos.color = Color.blue;
        //               Gizmos.DrawWireSphere (_movePosition, 2f);
        //           }
        //       }

        /**
         * 判断指定坐标是不是比当前位置离目标点更近
         */
        private bool IsNear(Vector3 currentPosition, Vector3 position)
        {
            switch (_moveStatus)
            {
                case MoveStatus.Position:
                    return Vector3.Distance(currentPosition, _movePosition) > Vector3.Distance(position, _movePosition);
                case MoveStatus.Target:
                    return Vector3.Distance(currentPosition, _moveTarget.GetPosition()) >
                           Vector3.Distance(position, _moveTarget.GetPosition());
                default:
                    return false;
            }
        }

        public void Amend(Vector3 currentPosition, Vector3 position)
        {
            //            if (IsNear(currentPosition, position))
            //            {
            _offsetPosition = position - currentPosition;
            var d = Vector3.Distance(position, currentPosition);
            // 偏移距离越远 速度越快 偏移越小 速度越慢 可以调整曲线函数
            _surplusFrameTime = _totalFrameTime = MathTools.easeInExpo(_maxFrameTime, _minFrameTime, 1 / d);

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveAmendData
                {
                    _offsetPosition = _offsetPosition, _totalFrameTime = _totalFrameTime,
                    _surplusFrameTime = _surplusFrameTime, _maxFrameTime = _maxFrameTime, _minFrameTime = _minFrameTime
                });
            //            }
        }

        public Vector3 GetCurrentTarget()
        {
            return lastPosition;
        }

        private void SetTarget(UnityObject target)
        {
            if (target == null) return;
            if (_moveStatus == MoveStatus.Target && _moveTarget == target) return;
            _moveStatus = MoveStatus.Target;
            _moveTarget = target;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new MoveStatusData { status = MoveStatus.Target });
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveData { target = target.GetEntity(), speed = _self.GetMoveSpeed() });
        }

        private void SetPosition(Vector3 position)
        {
            if (_moveStatus == MoveStatus.Position && _movePosition == position) return;
            _moveStatus = MoveStatus.Position;
            _movePosition = position;

            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new MoveStatusData { status = MoveStatus.Position });
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveData { position = position, speed = _self.GetMoveSpeed() });
        }

        public void SetVehicle(UnityObject e)
        {
            var entity = _self.GetEntity();
            if (e == null)
            {
                if (ObjectService.EntityManager.HasComponent<MoveVehicleData>(entity))
                    ObjectService.EntityManager.RemoveComponent<MoveVehicleData>(entity);
            }
            else
            {
                ObjectService.EntityManager.AddComponentData(entity,
                    new MoveVehicleData { target = e.GetEntity(), radius = e.GetRadius() });
            }
        }

        public void AddDynamicBlocks(UnityObject e, ColliderType type, bool close = true)
        {
            var entity = e.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveHoldData { close = close, type = type, radius = e.GetRadius() });
        }

        public void AddStaticBlocks(Vector3 position, ColliderType type, float size, bool close = true)
        {
            var entity = ObjectService.EntityManager.CreateEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveHoldData { close = close, type = type, radius = size });
            // todo Rotation
            ObjectService.EntityManager.AddComponentData(entity, new LocalTransform { Position = position });
        }

        public void MoveDirection(Vector3 d)
        {
            _self.SetPosition(_self.GetPosition() + d * Time.deltaTime * _self.GetMoveSpeed());
        }

        public void MoveTo(Vector3 position, EaseType easeType, float time)
        {
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new AnimationData { from = 0, to = 1, time = time, easeType = easeType });
            ObjectService.EntityManager.AddComponentData(entity,
                new AnimationTranslationData { start = _self.GetPosition(), end = position });
            if (time > 0) Frozen(time);
        }

        public bool IsMoving()
        {
            var entity = _self.GetEntity();
            if (ObjectService.EntityManager.HasComponent<AnimationData>(entity))
                return !ObjectService.EntityManager.GetComponentData<AnimationData>(entity).end;
            if (ObjectService.EntityManager.HasComponent<MoveStatusData>(entity))
                return moving;
            return false;
        }

        public void Frozen(float time)
        {
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new ActionFrozenData { lastTime = ObjectService.EntityManager.World.Time.ElapsedTime + time });
        }

        public void Hold()
        {
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new ActionHoldData());
        }

        public void Pass()
        {
            var entity = _self.GetEntity();
            ObjectService.EntityManager.RemoveComponent<ActionHoldData>(entity);
        }

        public void Stop()
        {
            _lastMoveStatus = _moveStatus;
            _moveStatus = MoveStatus.Stop;
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new MoveStatusData { status = MoveStatus.Stop });
        }

        public void Resume()
        {
            _moveStatus = _lastMoveStatus;
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new MoveStatusData { status = _lastMoveStatus });
            ObjectService.EntityManager.RemoveComponent<ActionFrozenData>(entity);
        }

        public void Cancel()
        {
            _moveStatus = MoveStatus.None;
            var entity = _self.GetEntity();
            ObjectService.EntityManager.AddComponentData(entity, new MoveStatusData { status = MoveStatus.None });
            moving = false;
            _self.OnMoveStop();
        }

        public void ChangeSpeed()
        {
            var entity = _self.GetEntity();
            if (ObjectService.EntityManager.HasComponent<MoveData>(entity)) return;
            var data = ObjectService.EntityManager.GetComponentData<MoveData>(entity);
            data.speed = _self.GetMoveSpeed();
            ObjectService.EntityManager.SetComponentData(entity, data);
        }

        /// <summary>
        ///     移动到指定坐标
        /// </summary>
        /// <param name="position"></param>
        /// <param name="append"></param>
        public void MovePosition(Vector3 position)
        {
            UpdatePath(position);
            if (moving)
            {
                _self.OnMoveStart();
                _moveTarget = null;
            }
        }

        /// <summary>
        ///     移动到指定目标
        /// </summary>
        /// <param name="obj"></param>
        public void MoveTarget(UnityObject obj)
        {
            UpdatePath(obj);
            if (moving)
            {
                _self.OnMoveStart();
                _moveTarget = obj;
            }
        }

        private Vector3[] FindPosition(Vector3 currentPosition, Vector3 position)
        {
            var tmp = TerrainService.single.FindPosition(currentPosition, position);
            if (tmp != null) TerrainService.single.FixPaths(ref tmp, _self.GetKeepRadius());

            return tmp;
        }

        private void UpdatePath(UnityObject obj)
        {
            if (obj == null || !obj.IsExist())
            {
                Debug.LogError("move target error");
                if (_notice != null) _notice.StopWithError();
                moving = false;
                return;
            }

            UpdatePath(obj.GetPosition());
        }

        private void UpdatePath(Vector3 position)
        {
            var tmpPaths = FindPosition(_self.GetPosition(), position);
            if (tmpPaths == null || tmpPaths.Length == 0)
            {
                Debug.LogError("move position error");
                lastIndex = -1;
                moving = false;
                if (_notice != null) _notice.StopWithError();
                return;
            }

            paths = tmpPaths;
            lastPosition = paths[0];
            lastIndex = 0;
            lastTime = Time.time;
            moving = true;
        }

        public void OnUpdate()
        {
            if (!moving) return;
            if (_moveTarget != null && Time.time - lastTime > findTime) UpdatePath(_moveTarget);

            if (lastIndex >= 0)
            {
                var d = 0.1f;
                if (_moveTarget != null && lastIndex == paths.Length - 1)
                {
                    LookTarget(_moveTarget);
                    d = _moveTarget.GetRadius() + _self.GetKeepRadius();
                }
                else
                {
                    LookPosition(lastPosition);
                }

                lastIndex = Vector3.Distance(_self.GetPosition(), lastPosition) <= d ? lastIndex + 1 : lastIndex;
                if (lastIndex == paths.Length)
                {
                    Debug.LogError("move success");
                    Stop();
                    moving = false;
                    lastIndex = -1;
                    if (_notice != null) _notice.StopWithSuccess();
                    return;
                }

                lastPosition = paths[lastIndex];
                SetPosition(lastPosition);
            }
        }

        public interface Notice
        {
            void StopWithError();
            void StopWithSuccess();
        }
    }
}